import pandas as pd
import requests
import geopandas as gpd
import os
from bs4 import BeautifulSoup
def download_file(url, filename):
if filename in os.listdir():
print("Fichier déjà présent")
return None
response = requests.get(url)
with open(filename, 'wb') as f:
f.write(response.content)
print('Fichier téléchargé')
def get_df(url, filename, encoding='utf-8'):
download_file(url, filename)
if filename.endswith('csv'):
return pd.read_csv(filename, encoding=encoding, sep=';')
elif filename.endswith('geojson'):
return gpd.read_file(filename)
communes = [
"Dardilly",
"Lyon",
"Villeurbanne",
"Saint-Genis-Laval",
"Caluire-et-Cuire",
"Vaulx-en-Velin",
"Écully",
"Rillieux-la-Pape",
"Saint-Didier-au-Mont-d'Or",
]
len(communes)
9
### INsee mais c'est tout pété leur système d'inscription
equipements_url = "https://download.data.grandlyon.com/wfs/rdata?SERVICE=WFS&VERSION=2.0.0&request=GetFeature&typename=urbalyon.recenseqptsport&outputFormat=application/json; subtype=geojson&SRSNAME=EPSG:4171"
equipements_filename = "equipements_sportifs_urba_lyon.geojson"
es = get_df(equipements_url, equipements_filename)
Fichier déjà présent
es_columns = ['nom', 'type', 'categorie', 'installation', 'commune', 'geometry',]
es = es[es_columns]
# remplacer Lyon Xème par Lyon
es.commune = es.commune.apply(lambda s: "Lyon" if s.startswith('Lyon') else s)
# filtrer sur les communes du comité
es = es[es.commune.isin(communes)]
es.head()
| nom | type | categorie | installation | commune | geometry | |
|---|---|---|---|---|---|---|
| 36 | Hall de tennis | Court de tennis | Courts de tennis | Stade Henri Cochet/Fitness Parc | Caluire-et-Cuire | POINT (4.83777 45.79185) |
| 37 | salle de musculation | Salle de musculation/cardiotraining | Salles de pratiques collectives | You Can Fit | Caluire-et-Cuire | POINT (4.83409 45.78693) |
| 38 | Les quais de saône | Boucle de randonnée | Nature | Circuits de Randonnees | Caluire-et-Cuire | POINT (4.83646 45.78780) |
| 39 | aviron club de lyon caluire | Site d'activités aquatiques et nautiques | Nature | Aviron Club de Lyon Caluire | Caluire-et-Cuire | POINT (4.82767 45.79466) |
| 40 | SALLE DE BOXE BATAG | Salle de boxe | Salles de pratiques collectives | Salle de Boxe Batag | Vaulx-en-Velin | POINT (4.91805 45.78573) |
# compter par commune
es.groupby('commune').count()[['nom']]
| nom | |
|---|---|
| commune | |
| Caluire-et-Cuire | 71 |
| Dardilly | 52 |
| Lyon | 863 |
| Rillieux-la-Pape | 90 |
| Saint-Didier-au-Mont-d'Or | 18 |
| Saint-Genis-Laval | 46 |
| Vaulx-en-Velin | 127 |
| Villeurbanne | 288 |
| Écully | 77 |
es.explore(column='commune', tiles="Stamen Toner")
parcs_url = "https://download.data.grandlyon.com/wfs/grandlyon?SERVICE=WFS&VERSION=2.0.0&request=GetFeature&typename=com_donnees_communales.comparcjardin_1_0_0&outputFormat=application/json; subtype=geojson&SRSNAME=EPSG:4171"
parcs_filename = "parcs_metropole.geojson"
parcs = get_df(parcs_url, parcs_filename)
Fichier déjà présent
parcs['surface_m2'] = parcs['geometry'].to_crs({'init': 'epsg:3395'}).map(lambda p: p.area)
parcs[['commune', "nom", "surf_tot_m2",'geometry']].explore(tiles='Stamen Toner', column='commune')
parcs_surface = parcs.groupby('commune').sum(numeric_only=True)[['surf_tot_m2']]
parcs_surface['surface_km2'] = parcs_surface.surf_tot_m2 / 10**6
parcs_surface[parcs_surface.index.isin(communes)]
| surf_tot_m2 | surface_km2 | |
|---|---|---|
| commune | ||
| Lyon | 3.842108e+06 | 3.842108 |
| Rillieux-la-Pape | 9.539670e+05 | 0.953967 |
| Saint-Didier-au-Mont-d'Or | 2.361500e+04 | 0.023615 |
| Saint-Genis-Laval | 1.063895e+05 | 0.106390 |
| Vaulx-en-Velin | 6.351486e+06 | 6.351486 |
| Villeurbanne | 6.557329e+05 | 0.655733 |
parcs_nombres = parcs.groupby('commune').count()[['nom']]
parcs_nombres[parcs_nombres.index.isin(communes)]
| nom | |
|---|---|
| commune | |
| Lyon | 296 |
| Rillieux-la-Pape | 15 |
| Saint-Didier-au-Mont-d'Or | 2 |
| Saint-Genis-Laval | 4 |
| Vaulx-en-Velin | 35 |
| Villeurbanne | 71 |
bdv_url = "https://download.data.grandlyon.com/wfs/grandlyon?SERVICE=WFS&VERSION=2.0.0&request=GetFeature&typename=ter_territoire.terbureauvote&outputFormat=application/json; subtype=geojson&SRSNAME=EPSG:4171"
bdv_filename = "bdv.geojson"
bdv = get_df(bdv_url, bdv_filename)
Fichier déjà présent
# remplacer Lyon Nème par Lyon
bdv.commune = bdv.commune.apply(lambda s: 'Lyon' if s.startswith('Lyon') else s)
bdv_ecully_url = "https://download.data.grandlyon.com/wfs/grandlyon?SERVICE=WFS&VERSION=2.0.0&request=GetFeature&typename=ecully.bureauvote&outputFormat=application/json; subtype=geojson&SRSNAME=EPSG:4171"
bdv_ecully_filename = "bdv_ecully.geojson"
bdv_ecully = get_df(bdv_ecully_url, bdv_ecully_filename)
Fichier déjà présent
# fix le nom de Écully dans ce fichier pour qu'il match avec la liste des communes
bdv_ecully.commune = bdv_ecully.commune.str.replace('E', 'É')
communes
['Dardilly', 'Lyon', 'Villeurbanne', 'Saint-Genis-Laval', 'Caluire-et-Cuire', 'Vaulx-en-Velin', 'Écully', 'Rillieux-la-Pape', "Saint-Didier-au-Mont-d'Or"]
bdv_vaulx_url = "https://download.data.grandlyon.com/wfs/grandlyon?SERVICE=WFS&VERSION=2.0.0&request=GetFeature&typename=vaulx.bureauvote_latest&outputFormat=application/json; subtype=geojson&SRSNAME=EPSG:4171"
bdv_vaulx_filename = "bdv_vaulx.geojson"
bdv_vaulx = get_df(bdv_vaulx_url, bdv_vaulx_filename)
Fichier déjà présent
bdv_dardilly_url = "https://download.data.grandlyon.com/wfs/grandlyon?SERVICE=WFS&VERSION=2.0.0&request=GetFeature&typename=dardilly.bureauvote&outputFormat=application/json; subtype=geojson&SRSNAME=EPSG:4171"
bdv_dardilly_filename = "bdv_dardilly.geojson"
bdv_dardilly = get_df(bdv_dardilly_url, bdv_dardilly_filename)
Fichier déjà présent
# on empile les fichiers de Écully, Vaulx-en-Velin et Dardilly dans le fichier général bdv
bdv = pd.concat([bdv, bdv_ecully])
bdv = pd.concat([bdv, bdv_vaulx])
bdv = pd.concat([bdv, bdv_dardilly])
# on ne garde que les communes du comité de suivi
bdv = bdv[bdv.commune.isin(communes)]
bdv.explore(column='commune', tiles="Stamen Toner")
bdv.commune.value_counts()
Lyon 304 Villeurbanne 80 Caluire-et-Cuire 37 Vaulx-en-Velin 20 Rillieux-la-Pape 18 Saint-Genis-Laval 14 Écully 11 Saint-Didier-au-Mont-d'Or 6 Dardilly 6 Name: commune, dtype: int64
codes_insee_url = "https://download.data.grandlyon.com/ws/grandlyon/adr_voie_lieu.adrcomgl/all.csv?maxfeatures=-1"
codes_insee_filename = "communes.csv"
codes = get_df(codes_insee_url, codes_insee_filename)
Fichier déjà présent
codes = codes[codes.nom.isin(communes)].reset_index()[['nom', 'insee']]
codes
| nom | insee | |
|---|---|---|
| 0 | Dardilly | 69072 |
| 1 | Rillieux-la-Pape | 69286 |
| 2 | Lyon | 69123 |
| 3 | Vaulx-en-Velin | 69256 |
| 4 | Écully | 69081 |
| 5 | Villeurbanne | 69266 |
| 6 | Saint-Genis-Laval | 69204 |
| 7 | Saint-Didier-au-Mont-d'Or | 69194 |
| 8 | Caluire-et-Cuire | 69034 |
# on ajoute les codes INSEE des arrondissements de Lyon
codes_lyon = pd.DataFrame([{"nom": "Lyon 1er Arrondissement" , "insee": 69381},
{"nom": "Lyon 2e Arrondissement", "insee": 69382},
{"nom": "Lyon 3e Arrondissement", "insee": 69383},
{"nom": "Lyon 4e Arrondissement", "insee": 69384},
{"nom": "Lyon 5e Arrondissement", "insee": 69385},
{"nom": "Lyon 6e Arrondissement", "insee": 69386},
{"nom": "Lyon 7e Arrondissement", "insee": 69387},
{"nom": "Lyon 8e Arrondissement", "insee": 69388},
{"nom": "Lyon 9e Arrondissement", "insee": 69389},
])
codes = pd.concat((codes, codes_lyon))
# on retire Lyon tout court pour éviter une 404 sur le site qu'on va scrapper
codes = codes[codes.nom != 'Lyon']
# on scrappe sur le site adresse.data.gouv.fr parce que je ne vois pas d'api pour avoir le nombre d'adresse par commune
base_url = "https://adresse.data.gouv.fr/commune/"
adresses = []
for row in codes.itertuples():
print(row.nom)
commune_url = base_url + str(row.insee)
response = requests.get(commune_url)
soup = BeautifulSoup(response.content)
nb_adresses = int(soup.find_all('div', class_="jsx-1548534320")[7].text)
adresses.append(nb_adresses)
Dardilly Rillieux-la-Pape Vaulx-en-Velin Écully Villeurbanne Saint-Genis-Laval Saint-Didier-au-Mont-d'Or Caluire-et-Cuire Lyon 1er Arrondissement Lyon 2e Arrondissement Lyon 3e Arrondissement Lyon 4e Arrondissement Lyon 5e Arrondissement Lyon 6e Arrondissement Lyon 7e Arrondissement Lyon 8e Arrondissement Lyon 9e Arrondissement
codes['adresses'] = adresses
adr_lyon = codes[codes.nom.str.startswith('Lyon')].adresses.sum()
lyon = pd.DataFrame([{'nom': 'Lyon', 'insee': '69123', 'adresses': adr_lyon}])
nb_adresses = pd.concat((codes, lyon)).reset_index().drop(columns='index')
nb_adresses
| nom | insee | adresses | |
|---|---|---|---|
| 0 | Dardilly | 69072 | 2080 |
| 1 | Rillieux-la-Pape | 69286 | 3892 |
| 2 | Vaulx-en-Velin | 69256 | 4966 |
| 3 | Écully | 69081 | 1886 |
| 4 | Villeurbanne | 69266 | 11816 |
| 5 | Saint-Genis-Laval | 69204 | 3759 |
| 6 | Saint-Didier-au-Mont-d'Or | 69194 | 1342 |
| 7 | Caluire-et-Cuire | 69034 | 4616 |
| 8 | Lyon 1er Arrondissement | 69381 | 2029 |
| 9 | Lyon 2e Arrondissement | 69382 | 2612 |
| 10 | Lyon 3e Arrondissement | 69383 | 6780 |
| 11 | Lyon 4e Arrondissement | 69384 | 2213 |
| 12 | Lyon 5e Arrondissement | 69385 | 3324 |
| 13 | Lyon 6e Arrondissement | 69386 | 2993 |
| 14 | Lyon 7e Arrondissement | 69387 | 4323 |
| 15 | Lyon 8e Arrondissement | 69388 | 5034 |
| 16 | Lyon 9e Arrondissement | 69389 | 3423 |
| 17 | Lyon | 69123 | 32731 |
### INsee mais c'est tout pété leur système d'inscription
contours = gpd.read_file('communes.geojson')
contours = contours[contours.nom.isin(communes)][["nom", "geometry"]]
contours
| nom | geometry | |
|---|---|---|
| 318 | Lyon | POLYGON ((4.81349 45.74776, 4.80244 45.75172, ... |
| 10475 | Vaulx-en-Velin | POLYGON ((4.91759 45.74829, 4.91898 45.75168, ... |
| 11861 | Villeurbanne | POLYGON ((4.89901 45.75245, 4.89368 45.75380, ... |
| 12075 | Saint-Genis-Laval | POLYGON ((4.82058 45.69558, 4.81750 45.69263, ... |
| 12091 | Rillieux-la-Pape | POLYGON ((4.87981 45.79532, 4.87744 45.79697, ... |
| 12240 | Caluire-et-Cuire | POLYGON ((4.84148 45.80344, 4.84325 45.80952, ... |
| 12258 | Dardilly | POLYGON ((4.73602 45.83738, 4.73803 45.83788, ... |
| 12262 | Écully | POLYGON ((4.78867 45.78953, 4.78577 45.78871, ... |
| 12313 | Saint-Didier-au-Mont-d'Or | POLYGON ((4.78226 45.81140, 4.78416 45.81371, ... |
contours.explore(tiles = None)
os.makedirs('contours', exist_ok=True)
communes[-1] = communes[-1].replace("'", "’")
communes
['Dardilly', 'Lyon', 'Villeurbanne', 'Saint-Genis-Laval', 'Caluire-et-Cuire', 'Vaulx-en-Velin', 'Écully', 'Rillieux-la-Pape', 'Saint-Didier-au-Mont-d’Or']
contours.iloc[-1, 0] = communes[-1]
contours
| nom | geometry | |
|---|---|---|
| 318 | Lyon | POLYGON ((4.81349 45.74776, 4.80244 45.75172, ... |
| 10475 | Vaulx-en-Velin | POLYGON ((4.91759 45.74829, 4.91898 45.75168, ... |
| 11861 | Villeurbanne | POLYGON ((4.89901 45.75245, 4.89368 45.75380, ... |
| 12075 | Saint-Genis-Laval | POLYGON ((4.82058 45.69558, 4.81750 45.69263, ... |
| 12091 | Rillieux-la-Pape | POLYGON ((4.87981 45.79532, 4.87744 45.79697, ... |
| 12240 | Caluire-et-Cuire | POLYGON ((4.84148 45.80344, 4.84325 45.80952, ... |
| 12258 | Dardilly | POLYGON ((4.73602 45.83738, 4.73803 45.83788, ... |
| 12262 | Écully | POLYGON ((4.78867 45.78953, 4.78577 45.78871, ... |
| 12313 | Saint-Didier-au-Mont-d’Or | POLYGON ((4.78226 45.81140, 4.78416 45.81371, ... |
# export des contours en SVG pour chaque commune
for city in communes:
filename = f'contours/{city}.shp'
print(filename)
contours[contours.nom == city].to_file(filename)
cmd = f"""svgis draw {filename} -o contours/{city}.svg \
--crs "+proj=aea +lat_1=20 +lat_2=60 +lat_0=40 \
+lon_0=-96 +x_0=0 +y_0=0 +datum=NAD83 +units=m +no_defs"
"""
os.system(cmd)
contours/Dardilly.shp contours/Lyon.shp contours/Villeurbanne.shp contours/Saint-Genis-Laval.shp contours/Caluire-et-Cuire.shp contours/Vaulx-en-Velin.shp contours/Écully.shp contours/Rillieux-la-Pape.shp contours/Saint-Didier-au-Mont-d’Or.shp
for file in os.listdir('contours'):
if not file.endswith('svg'):
os.unlink(f"contours/{file}")
adresse_sharepoint = "https://tubalyon.sharepoint.com/Documents%20partages/Forms/AllItems.aspx?viewpath=%2FDocuments%20partages%2FForms%2FAllItems%2Easpx&id=%2FDocuments%20partages%2FPROJETS%2FEN%20COURS%2FOPEN%20DATA%20COMMUNES%2F2022%2D2023%2FCOMIT%C3%89%20DE%20SUIVI%2FCONTOURS%5FSVG&viewid=62afc8d0%2D8661%2D48ee%2Da1f1%2Dd31a543b6fa8"
adresse_sharepoint
'https://tubalyon.sharepoint.com/Documents%20partages/Forms/AllItems.aspx?viewpath=%2FDocuments%20partages%2FForms%2FAllItems%2Easpx&id=%2FDocuments%20partages%2FPROJETS%2FEN%20COURS%2FOPEN%20DATA%20COMMUNES%2F2022%2D2023%2FCOMIT%C3%89%20DE%20SUIVI%2FCONTOURS%5FSVG&viewid=62afc8d0%2D8661%2D48ee%2Da1f1%2Dd31a543b6fa8'
communes[-1] = "Saint-Didier-au-Mont-d'Or"
## établissement recevant du public
erp_url = "https://download.data.grandlyon.com/wfs/rdata?SERVICE=WFS&VERSION=2.0.0&request=GetFeature&typename=sdmis.erp&outputFormat=application/json; subtype=geojson&SRSNAME=EPSG:4171"
erp_filename = "erp.geojson"
erp = get_df(erp_url, erp_filename)
Fichier déjà présent
erp.commune = erp.commune.apply(lambda s: 'LYON' if s.startswith('LYON') else s)
COMMUNES = [commune.replace('-', " ").upper() for commune in communes]
COMMUNES
['DARDILLY', 'LYON', 'VILLEURBANNE', 'SAINT GENIS LAVAL', 'CALUIRE ET CUIRE', 'VAULX EN VELIN', 'ÉCULLY', 'RILLIEUX LA PAPE', "SAINT DIDIER AU MONT D'OR"]
# on filtre sur nos communes
erp = erp[erp.commune.isin(COMMUNES)]
ecoles = erp[(erp.libelle.str.startswith('Lyc')) | erp.libelle.str.contains('aternelle') | erp.libelle.str.contains('ollège') | erp.libelle.str.contains('primaire') | erp.libelle.str.contains('élémentaire') | erp.libelle.str.contains('GS') ]
erp[erp.libelle.str.contains('ourette')]
| code | libelle | nature | categorie | type | types_secondaires | hebergement | effectif_total_reglementaire | adresse | code_postal | insee | commune | gid | geometry | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 3963 | E38100068000 | Collège du site de la Tourette | ERP | 2 | R | 800 | 80 boulevard de la Croix-Rousse | 69001 | 69381 | LYON | 3959 | POINT (4.82457 45.77339) |
ecoles.explore(color="purple", tiles="Stamen Toner")
ecoles.commune.value_counts()
LYON 276 VILLEURBANNE 66 VAULX EN VELIN 37 RILLIEUX LA PAPE 24 CALUIRE ET CUIRE 23 SAINT GENIS LAVAL 19 DARDILLY 14 ÉCULLY 14 SAINT DIDIER AU MONT D'OR 3 Name: commune, dtype: int64